package chat

import (
	"bytes"
	"chatroom/models"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"time"
)

const (
	writeWait      = 10 * time.Second    //从客户端读取信息等待的时间
	pongWait       = 60 * time.Second    //每个60s发送一条pong信息用以监测连接的状态
	pingPeriod     = (pongWait * 9) / 10 //发送ping信息的时间
	maxMessageSize = 512                 //设置最大发送的信息是512
)

/**接入的客户端*/
type Client struct {
	Con         *websocket.Conn
	Hub         *Hub        //此次只有hub的引用是为了方便获取hub进行操作
	SendMessage chan []byte //接收客户端的信息，并将该信息交给hub广播给每一个客户端
	Username    string      //用户名
} //Client维持每一个客户端的连接

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024, //设置读的缓存大小为1024
	WriteBufferSize: 1024, //设置写的缓存大小为1024
}

/**从客户接收消息*/
func (this *Client) ReaderMessage() {
	defer func() { //如果在读取过程中发生错误则会注销客户端，断开连接
		this.Hub.UnRegister <- this                          //注销客户端
		this.Hub.BroadCast <- []byte("out:" + this.Username) //发送注销的广播信息
		this.Con.Close()                                     //关闭客户端连接
		//修改当前用户的登录状态
		user := models.GetUserByName(this.Username)
		user.Islogin = false
		models.SetLoginState(&user)
		log.Println("修改用户得登录状态为离线。。。。。")
	}()
	//websocket连接的基本设置
	this.Con.SetReadLimit(maxMessageSize) //设置读取信息的上限
	this.Con.SetReadDeadline(time.Now().Add(pongWait))
	this.Con.SetPongHandler(func(pongMessage string) error {
		this.Con.SetReadDeadline(time.Now().Add(pongWait))
		log.Println("获取客户端响应的Pong信息....", pongMessage)
		return nil
	})
	for {
		_, message, err := this.Con.ReadMessage() //第一个返回值表示信息的类型,第二个参数表示信息内容，第三个参数表示是否有错误
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
				log.Println("非法错误:", err)
			}
			break
		}
		//没有错误则获取并解析信息内容
		message = bytes.TrimSpace(bytes.Replace(message, []byte{'\n'}, []byte{' '}, -1)) //去掉信息中的空格字符
		this.Hub.BroadCast <- []byte(this.Username + ":" + string(message))
	}
}

/**向客户端发送消息*/
func (this *Client) WriterMessage() {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()    //方法结束后则停止tiker定时器
		this.Con.Close() //方法结束后则关闭websocket连接
	}()
	for {
		select {
		case message, ok := <-this.SendMessage:
			//等待一段时间
			this.Con.SetWriteDeadline(time.Now().Add(writeWait)) //设置向客户端发送信息等等的时间
			if !ok {                                             //没有信息这
				//没有获取到信息则关闭连接
				this.Con.WriteMessage(websocket.CloseMessage, []byte{})
				return
			}
			//有信息这进行发送新处理
			w, err := this.Con.NextWriter(websocket.TextMessage)
			if err != nil { //有错误则返回
				log.Println("获取NextWriter错误:", err)
				return
			}
			log.Println("发送的信息是:", message)

			w.Write(message) //向客户些信息
			//将当前信息增加到信息队列
			n := len(this.SendMessage)
			for i := 0; i < n; i++ {
				w.Write([]byte{'\n'})
				w.Write(<-this.SendMessage)
			}

			if err := w.Close(); err != nil {
				log.Println("关闭Writer错误", err)
				return
			}
		case <-ticker.C:
			//每隔54秒向客户端发送一条ping信息已检测连接的状态
			this.Con.SetWriteDeadline(time.Now().Add(writeWait))
			if err := this.Con.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
				log.Println("发送Ping错误:", err)
				return
			}
			log.Println("向客户端发送了一条Ping命令.........")
		}
	}
}

/**注册客户端*/
func RegisterClient(w http.ResponseWriter, r *http.Request, hub *Hub, username string) {
	//将http或https升级为websocket协议
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Println(err)
		return
	}
	client := &Client{Con: conn, Hub: hub, SendMessage: make(chan []byte, 256), Username: username}
	hub.Register <- client
	hub.BroadCast <- []byte("in:" + username) //以s:开头的为系统信息
	log.Println("客户端注册成功", client)
	go client.WriterMessage() //启动一个go程,只要客户端有信息发送过来则将该信息推送到每个客户端
	client.ReaderMessage()    //读取客户端发送活过来的信息
}
